# system imports
import os, sys, errno 
import atexit, time, signal

# local imports
import log

class BaseDaemon(object):
	"""
	A daemon class to manage any independant service process.

	Usage: subclass the BaseDaemon class and override the start() and run() methods.
	Put your pre-daemonziation setup code in the overriden start() method. 
	For example, bind to a 
	socket and create long lived servce objects. The run() method is the entry
	point for service. run() is called after the process has been daemonized 
	and is running as an independant service. By default, it will just exit, 
	so you *must* override this with your entry point for your service.

	Overriding start() is optional, but it allows you to run code before
	the process has been daemonized and before run() is called. If you 
	override start(), you must call the :exc:`super`()'s start() method to daemonize 
	the service.
	
	===========  ============================================================
	Parameter  
	===========  ============================================================
	*pidfile*    The location for our pid file that this process will create
	*stdin*      File name to set the daemon processes stdin to 
	*stdout*     File name to set the daemon processes stdout to
	*stderr*     File name to set the daemon processes stderr to
	===========  ============================================================

	When a :doc:`basedaemon` instance is started, by calling the start() method, it will
	detach itself from the console and run in the background as a service. The PID of 
	the service process will be stored in *pidfile*.

	An example of inheriting from :doc:`basedaemon` and overriding the start() and 
	run() methods:
	::
		class ServerDaemon(BaseDaemon):

			def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
				BaseDaemon.__init__(self, pidfile, stdin, stdout, stderr )
			#__init__()

			def start(self):
				# Setup our listener socket.
				sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
				sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
				sock.setblocking(0)
				sock.bind((self.host, self.port))
				sock.listen(128)

				# Create our ioloop instance
				self.io_loop.add_handler(sock.fileno(), self._connection_ready, ioloop.IOLoop.READ | ioloop.IOLoop._EPOLLET)
				self.listen_sock = sock

				# daemonize !!!
				# This will call our run() last thing.
				BaseServer.start( self )
			#start()

			def run(self):
				ioloop.start()
			#run()

		#ServerDaemon

	"""

	def __init__(self, pidfile, procname='python_base_daemon',
			  stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
		"""docstring for __init__"""

		log.log( "BaseDaemon.__init__()", 'debug' )
		
		self.pidfile = pidfile
		self.stdin = stdin
		self.stdout = stdout
		self.stderr = stderr
		self.procname = procname
	#__init__()

	def daemonize(self):
		"""
		Do the UNIX double-fork magic, see Stevens' "Advanced 
		Programming in the UNIX Environment" for details (ISBN 0201563177)

		`ISBN 0201563177 on Amazon <http://www.amazon.com/gp/search/ref=sr_adv_b/?search-alias=stripbooks&unfiltered=1&field-keywords=&field-author=&field-title=&field-isbn=978-0201563177&field-publisher=&node=&field-p_n_condition-type=&field-feature_browse-bin=&field-binding_browse-bin=&field-subject=&field-language=&field-dateop=&field-datemod=&field-dateyear=&sort=relevanceexprank&Adv-Srch-Books-Submit.x=-181&Adv-Srch-Books-Submit.y=-192>`_

		"""
		log.log( "BaseDaemon::daemonize %s" % ( str( self ) ), 'info' )
		try: 
			pid = os.fork() 
			if pid > 0:
				# exit first parent
				log.log( "BaseDaemon::daemonize exit first parent", 'debug' )
				sys.exit(0) 
		except OSError, e:
			sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
			log.log("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
			sys.exit(1)
	
		# decouple from parent environment
		log.log( "BaseDaemon::daemonize decouple from parent.", 'debug' )
		os.chdir("/") 
		os.setsid() 
		os.umask(0) 
	
		# do second fork
		log.log( "BaseDaemon::daemonize second fork.", 'debug' )
		try: 
			pid = os.fork() 
			if pid > 0:
				# exit from second parent
				log.log( "BaseDaemon::daemonize exit second parent", 'debug' )
				sys.exit(0)
			else: 
				log.set_proc_name(self.procname)
				proc_name = log.get_proc_name()
				log.log("Starting %s" % (proc_name), 'info')

		except OSError as e:
			sys.stderr.write("pid:%s, fork #2 failed: %s, %s (%s)\n" % (
					str(pid), e, str(e.errno), e.strerror))
			log.log( "pid:%s, fork #2 failed: %s, %s (%s)\n" % (
					str(pid), e, str(e.errno), e.strerror), 'error' )
			sys.exit(1) 
	
		try:
			# redirect standard file descriptors
			log.log( "BaseDaemon::daemonize redirect file descriptors.", 'debug' )
			sys.stdout.flush()
			sys.stderr.flush()
			si = file(self.stdin, 'r')
			so = file(self.stdout, 'a+')
			se = file(self.stderr, 'a+', 0)
			os.dup2(si.fileno(), sys.stdin.fileno())
			os.dup2(so.fileno(), sys.stdout.fileno())
			os.dup2(se.fileno(), sys.stderr.fileno())
		
			# write pidfile
			log.log( "BaseDaemon::daemonize write pid file.", 'debug' )
			atexit.register(self.delpid)
			pid = str(os.getpid())
			open(self.pidfile,'w+').write("%s\n" % pid)

			#log.log( "BaseDaemon::daemonized, changing to user:%s, group:%s." % ( self.user, self.group ), 'debug' )
			## pwd.getpwnam will raise a KeyError if the user/group cannot be found.
			#log_uid = pwd.getpwnam(self.user)[2]
			#log_gid = pwd.getpwnam(self.group)[3]
			#os.chmod( self.pidfile, 0644 )
			#os.chown( self.pidfile, log_uid, log_gid )
			#os.setgid( log_uid )
			#os.setuid( log_gid )
		except Exception, e:
			log.log( "BaseDaemon::daemonized, Pokemon exception: %s" %e, 'error' )

		log.log( "BaseDaemon::daemonized, done.", 'debug' )
	
	def delpid(self):
		"""
		Delete this processes PID file.

		.. note::

			SELinux will not let root delete any processes pid file root does not
			own, so *delpid()* will change to the user/group the service is
			set to run as and then delete the pid file. 
			This issue occurs when the SELinux security context type initrc_var_run_t
			is set on the /var/run directory.

		"""

		log.log( "BaseDaemon::delpid: deleting %s. uid:%d, gid:%d. file state:%s" % (self.pidfile, os.getuid(), os.getgid(), os.stat(self.pidfile)), 'debug' )

		try:
			log.log( "BaseDaemon::delpid deleting %s" % self.pidfile, 'critical' )
			os.remove(self.pidfile)
		except Exception, e:
			log.log( e )
			raise

	def start(self):
		"""
		Start the daemon.
		
		This will see if a pid file already exists, if it does it will complain, but 
		continue on. Then self.daemonize() and self.run() will be called in that order.
		"""

		log.log( "BaseDaemon::start", 'info' )
		# Check for a pidfile to see if the daemon already runs
		try:
			pf = file(self.pidfile,'r')
			pid = int(pf.read().strip())
			pf.close()
		except IOError:
			pid = None
	
		if pid:
			message = "BaseDaemon::start pidfile %s already exist. BaseDaemon already running?\n"
			log.log( message % self.pidfile, 'error' )
			#sys.stderr.write(message % self.pidfile)
			#sys.exit(1)
		
		# Start the daemon
		self.daemonize()
		self.run()

		#if run exits, clean up.
		self.stop()

	def stop(self):
		"""
		Stop the running daemon represented by this instance.

		First we'll check for a PID file, if doesn't exist we'll write a message
		to stdout and exit. If a PID is found, we will try to kill the process
		with the PID by sending it the :exc:`signal`.SIGTERM signal and then 
		we will remove the PID file as well.
		"""
		log.log( "BaseDaemon::stop", 'debug' )

		# Get the pid from the pidfile
		try:
			pf = file(self.pidfile,'r')
			pid = int(pf.read().strip())
			pf.close()
		except IOError:
			pid = None
	
		if not pid:
			message = "pidfile %s does not exist. BaseDaemon not running?\n"
			sys.stderr.write(message % self.pidfile)
			return # not an error in a restart

		# Try killing the daemon process	
		try:
			while 1:
				os.kill(pid, signal.SIGTERM)
				time.sleep(0.1)
		except OSError, err:
			err = str(err)
			if err.find("No such process") > 0:
				if os.path.exists(self.pidfile):
					self.delpid()
			else:
				print str(err)
				sys.exit(1)

	def restart(self):
		"""
		Restart the daemon.

		This just calls start and stop:
		::

			self.stop()
			self.start()

		"""
		log.log( "BaseDaemon::restart", 'info' )
		self.stop()
		self.start()

	def run(self):
		"""
		Starts the zero mq loop.
		Use the stop() method to stop this process.
		"""

		log.log( "BaseDaemon::run bailing out immediately", 'debug' )
#BaseDaemon
